package com.tbynet.jwp.framework.core;

import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;

/**
 * 基础拦截器类
 *
 * 核心职责：
 * 1. 提供拦截器通用方法和工具函数
 * 2. 统一的权限验证和访问控制
 * 3. 会话管理和用户信息处理
 * 4. 安全事件记录和异常处理
 *
 * 设计原则：
 * - 提供可重用的拦截器功能
 * - 统一的错误处理和日志记录
 * - 支持细粒度的权限控制
 * - 完善的性能监控
 *
 * @author 佰亿互联
 * @version 1.0
 * @since 2025-11-04
 */
public abstract class JwpInterceptor {

    /** 日志记录器 */
    protected final Log log = Log.getLog(this.getClass());

    // ========== 配置常量 ==========

    /** 默认公开接口前缀 */
    protected static final String[] DEFAULT_PUBLIC_INTERFACES = {
            "/public/",
            "/api/public/",
            "/login",
            "/register",
            "/captcha",
            "/error"
    };

    /** 慢请求阈值（毫秒） */
    protected static final long SLOW_REQUEST_THRESHOLD = 1000;

    // ========== 通用拦截器方法 ==========

    /**
     * 获取客户端IP地址
     *
     * @param inv 拦截器调用对象
     * @return 客户端IP地址
     */
    protected String getClientIp(Invocation inv) {
        Controller controller = inv.getController();

        String ip = controller.getHeader("X-Real-IP");
        if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = controller.getHeader("X-Forwarded-For");
        }
        if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = controller.getHeader("Proxy-Client-IP");
        }
        if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = controller.getHeader("WL-Proxy-Client-IP");
        }
        if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = controller.getRequest().getRemoteAddr();
        }

        // 处理X-Forwarded-For多个IP的情况
        if (StrKit.notBlank(ip) && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }

    /**
     * 获取用户代理信息
     *
     * @param inv 拦截器调用对象
     * @return 用户代理字符串
     */
    protected String getUserAgent(Invocation inv) {
        Controller controller = inv.getController();
        String userAgent = controller.getHeader("user-agent");
        return userAgent != null ? userAgent : "";
    }

    /**
     * 记录请求开始日志
     *
     * @param inv 拦截器调用对象
     * @param interceptorName 拦截器名称
     */
    protected void logRequestStart(Invocation inv, String interceptorName) {
        String actionKey = inv.getActionKey();
        String clientIp = getClientIp(inv);

        log.debug("拦截器[%s]开始处理请求，ActionKey: %s, IP: %s",
                interceptorName, actionKey, clientIp);
    }

    /**
     * 记录请求完成日志
     *
     * @param inv 拦截器调用对象
     * @param interceptorName 拦截器名称
     * @param startTime 开始时间
     */
    protected void logRequestComplete(Invocation inv, String interceptorName, long startTime) {
        String actionKey = inv.getActionKey();
        long cost = System.currentTimeMillis() - startTime;

        if (cost > SLOW_REQUEST_THRESHOLD) {
            log.warn("拦截器[%s]处理慢请求，ActionKey: %s, 耗时: %sms",
                    interceptorName, actionKey, cost);
        } else {
            log.debug("拦截器[%s]请求处理完成，ActionKey: %s, 耗时: %sms",
                    interceptorName, actionKey, cost);
        }
    }

    /**
     * 记录安全事件
     *
     * @param eventType 事件类型
     * @param userId 用户ID
     * @param actionKey 操作键
     * @param clientIp 客户端IP
     * @param details 事件详情
     */
    protected void logSecurityEvent(String eventType, Object userId, String actionKey,
                                    String clientIp, String details) {
        log.warn("安全事件 - 类型: %s, 用户ID: %s, ActionKey: %s, IP: %s, 详情: %s",
                eventType, userId, actionKey, clientIp, details);

        // TODO: 可以将安全事件保存到数据库或安全审计系统
    }

    /**
     * 记录权限事件
     *
     * @param eventType 权限事件类型
     * @param userId 用户ID
     * @param actionKey 操作键
     * @param resource 资源标识
     * @param result 结果
     */
    protected void logPermissionEvent(String eventType, Object userId, String actionKey,
                                      String resource, String result) {
        log.info("权限事件 - 类型: %s, 用户ID: %s, ActionKey: %s, 资源: %s, 结果: %s",
                eventType, userId, actionKey, resource, result);
    }

    // ========== 权限验证方法 ==========

    /**
     * 检查是否为公开接口
     *
     * @param actionKey 操作键
     * @param publicInterfaces 公开接口列表
     * @return 是否为公开接口
     */
    protected boolean isPublicInterface(String actionKey, String[] publicInterfaces) {
        if (publicInterfaces == null || publicInterfaces.length == 0) {
            publicInterfaces = DEFAULT_PUBLIC_INTERFACES;
        }

        for (String publicInterface : publicInterfaces) {
            if (actionKey.startsWith(publicInterface)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查访问频率
     *
     * @param key 访问标识
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否允许访问
     */
    protected boolean checkAccessRate(String key, int maxRequests, int timeWindow) {
        // TODO: 实现访问频率检查逻辑
        // 可以使用Redis或内存缓存来实现

        log.debug("访问频率检查，key: %s, maxRequests: %s, timeWindow: %s",
                key, maxRequests, timeWindow);
        return true;
    }

    /**
     * 验证必需参数
     *
     * @param controller 控制器对象
     * @param params 参数名数组
     * @return 是否验证通过
     */
    protected boolean validateRequiredParams(Controller controller, String... params) {
        for (String param : params) {
            if (StrKit.isBlank(controller.getPara(param))) {
                log.warn("必需参数验证失败，参数: %s, ActionKey: %s",
                        param, controller.getRequest().getRequestURI());
                return false;
            }
        }
        return true;
    }

    // ========== 会话管理方法 ==========

    /**
     * 获取会话ID
     *
     * @param controller 控制器对象
     * @param sessionIdName 会话ID名称
     * @return 会话ID
     */
    protected String getSessionId(Controller controller, String sessionIdName) {
        return controller.getCookie(sessionIdName);
    }

    /**
     * 验证会话有效性
     *
     * @param sessionId 会话ID
     * @param sessionValidator 会话验证器
     * @return 会话是否有效
     */
    protected boolean validateSession(String sessionId, SessionValidator sessionValidator) {
        if (StrKit.isBlank(sessionId)) {
            return false;
        }

        try {
            return sessionValidator.validate(sessionId);
        } catch (Exception e) {
            log.error("会话验证异常", e);
            return false;
        }
    }

    /**
     * 清理会话数据
     *
     * @param controller 控制器对象
     * @param sessionIdName 会话ID名称
     */
    protected void clearSession(Controller controller, String sessionIdName) {
        controller.removeCookie(sessionIdName);
    }

    // ========== 错误处理方法 ==========

    /**
     * 处理未认证请求
     *
     * @param controller 控制器对象
     * @param inv 拦截器调用对象
     * @param loginUrl 登录页面URL
     */
    protected void handleUnauthenticated(Controller controller, Invocation inv, String loginUrl) {
        String actionKey = inv.getActionKey();
        String clientIp = getClientIp(inv);

        log.debug("用户未认证，ActionKey: %s, IP: %s", actionKey, clientIp);

        // 构建重定向URL
        String redirectUrl = buildRedirectUrl(inv);

        // 记录安全事件
        logSecurityEvent("UNAUTHENTICATED_ACCESS", null, actionKey, clientIp,
                "重定向到: " + loginUrl);

        // 重定向到登录页面
        controller.redirect(loginUrl + "?redirect_url=" + redirectUrl);
    }

    /**
     * 处理权限拒绝
     *
     * @param controller 控制器对象
     * @param inv 拦截器调用对象
     * @param user 用户对象
     * @param errorPage 错误页面路径
     */
    protected void handlePermissionDenied(Controller controller, Invocation inv,
                                          Object user, String errorPage) {
        String actionKey = inv.getActionKey();
        String clientIp = getClientIp(inv);
        String userId = user != null ? String.valueOf(user) : "unknown";

        log.warn("权限拒绝，用户ID: %s, ActionKey: %s, IP: %s",
                userId, actionKey, clientIp);

        // 设置错误信息
        controller.set("error", "permission");
        controller.set("errorMessage", "您没有权限访问该页面");
        controller.set("errorCode", 403);

        // 渲染错误页面
        if (StrKit.notBlank(errorPage)) {
            controller.render(errorPage);
        } else {
            controller.render("/error.html");
        }

        // 记录安全事件
        logSecurityEvent("PERMISSION_DENIED", userId, actionKey, clientIp,
                "权限拒绝，访问资源: " + actionKey);
    }

    /**
     * 处理系统异常
     *
     * @param controller 控制器对象
     * @param inv 拦截器调用对象
     * @param e 异常对象
     * @param errorPage 错误页面路径
     */
    protected void handleSystemException(Controller controller, Invocation inv,
                                         Exception e, String errorPage) {
        String actionKey = inv.getActionKey();
        String clientIp = getClientIp(inv);

        log.error("拦截器系统异常，ActionKey: %s, IP: %s", actionKey, clientIp, e);

        // 判断是否为AJAX请求
        boolean isAjax = "XMLHttpRequest".equals(controller.getHeader("X-Requested-With"));

        if (isAjax) {
            controller.renderJson("{\"code\":\"SYSTEM_ERROR\",\"msg\":\"系统异常，请稍后重试\"}");
        } else {
            controller.set("error", "system");
            controller.set("errorMessage", "系统异常，请稍后重试");
            if (StrKit.notBlank(errorPage)) {
                controller.render(errorPage);
            } else {
                controller.render("/error.html");
            }
        }

        // 记录安全事件
        logSecurityEvent("SYSTEM_EXCEPTION", null, actionKey, clientIp,
                "异常信息: " + e.getMessage());
    }

    // ========== 工具方法 ==========

    /**
     * 构建重定向URL
     *
     * @param inv 拦截器调用对象
     * @return 重定向URL
     */
    protected String buildRedirectUrl(Invocation inv) {
        String redirectUrl = inv.getActionKey();
        String queryString = inv.getController().getRequest().getQueryString();

        if (StrKit.notBlank(queryString)) {
            redirectUrl += "?" + queryString;
        }

        return redirectUrl;
    }

    /**
     * 隐藏敏感信息
     *
     * @param str 原始字符串
     * @param start 开始保留位数
     * @param end 结束保留位数
     * @return 隐藏后的字符串
     */
    protected String hideSensitiveInfo(String str, int start, int end) {
        if (StrKit.isBlank(str) || str.length() <= start + end) {
            return "***";
        }

        String startStr = str.substring(0, start);
        String endStr = str.substring(str.length() - end);
        return startStr + "***" + endStr;
    }

    /**
     * 获取请求基本信息
     *
     * @param inv 拦截器调用对象
     * @return 请求基本信息字符串
     */
    protected String getRequestInfo(Invocation inv) {
        Controller controller = inv.getController();
        StringBuilder info = new StringBuilder();

        info.append("ActionKey: ").append(inv.getActionKey());
        info.append(", Method: ").append(controller.getRequest().getMethod());
        info.append(", IP: ").append(getClientIp(inv));
        info.append(", UserAgent: ").append(getUserAgent(inv));

        return info.toString();
    }

    // ========== 内部接口和类 ==========

    /**
     * 会话验证器接口
     */
    @FunctionalInterface
    public interface SessionValidator {
        /**
         * 验证会话有效性
         *
         * @param sessionId 会话ID
         * @return 会话是否有效
         */
        boolean validate(String sessionId);
    }

    /**
     * 权限验证器接口
     */
    @FunctionalInterface
    public interface PermissionValidator {
        /**
         * 验证权限
         *
         * @param user 用户对象
         * @param resource 资源标识
         * @param action 操作类型
         * @return 是否具有权限
         */
        boolean validate(Object user, String resource, String action);
    }

    /**
     * 拦截器配置类
     */
    public static class InterceptorConfig {
        private String[] publicInterfaces;
        private String loginUrl;
        private String errorPage;
        private int maxRequestsPerMinute;

        public InterceptorConfig() {
            this.publicInterfaces = DEFAULT_PUBLIC_INTERFACES;
            this.loginUrl = "/login";
            this.errorPage = "/error.html";
            this.maxRequestsPerMinute = 100;
        }

        // Getter和Setter方法
        public String[] getPublicInterfaces() {
            return publicInterfaces;
        }

        public InterceptorConfig setPublicInterfaces(String[] publicInterfaces) {
            this.publicInterfaces = publicInterfaces;
            return this;
        }

        public String getLoginUrl() {
            return loginUrl;
        }

        public InterceptorConfig setLoginUrl(String loginUrl) {
            this.loginUrl = loginUrl;
            return this;
        }

        public String getErrorPage() {
            return errorPage;
        }

        public InterceptorConfig setErrorPage(String errorPage) {
            this.errorPage = errorPage;
            return this;
        }

        public int getMaxRequestsPerMinute() {
            return maxRequestsPerMinute;
        }

        public InterceptorConfig setMaxRequestsPerMinute(int maxRequestsPerMinute) {
            this.maxRequestsPerMinute = maxRequestsPerMinute;
            return this;
        }
    }
}